#ifndef __MY_UTIL__
#define __MY_UTIL__
#include "jsoncpp/json/json.h"
#include <cassert>
#include <sstream>
#include <memory>
#include "bundle.h"
#include <iostream>
#include <experimental/filesystem>
#include <string>
#include <sys/stat.h>
#include <vector>
#include <fstream>
#include <mysql/mysql.h>
#include <sw/redis++/redis++.h>

namespace cloud
{
    namespace fs = std::experimental::filesystem;
    class FileUtil
    {
    private:
        std::string filename_;

    public:
        FileUtil(const std::string &filename) : filename_(filename) {}

        ////////////////////////////////////////////
        // 文件操作
        //  获取文件大小
        int64_t FileSize()
        {
            struct stat s;
            auto ret = stat(filename_.c_str(), &s);
            if (ret == -1)
            {
                perror("Get file size failed");
                return -1;
            }
            return s.st_size;
        }
        // 获取文件最近访问时间
        time_t LastAccessTime()
        {
            struct stat s;
            auto ret = stat(filename_.c_str(), &s);
            if (ret == -1)
            {
                perror("Get file access time failed");
                return -1;
            }
            return s.st_atime;
        }

        // 获取文件最近修改时间
        time_t LastModifyTime()
        {
            struct stat s;
            auto ret = stat(filename_.c_str(), &s);
            if (ret == -1)
            {
                perror("Get file modify time failed");
                return -1;
            }
            return s.st_mtime;
        }

        // 从路径中解析出文件名
        std::string FileName()
        {
            auto pos = filename_.find_last_of("/");
            if (pos == std::string::npos)
                return filename_;
            return filename_.substr(pos + 1, std::string::npos);
        }

        // 从文件POS处获取len长度字符给content
        bool GetPosLen(std::string *content, size_t pos, size_t len)
        {
            // 判断要求数据内容是否符合文件大小
            if (pos + len > FileSize())
            {
                std::cout << "Get filesize error" << std::endl;
                return false;
            }

            // 打开文件
            std::ifstream ifs;
            ifs.open(filename_.c_str(), std::ios::binary);
            if (ifs.is_open() == false)
            {
                std::cout << "file open error" << std::endl;
                return false;
            }

            // 读入content
            ifs.seekg(pos, std::ios::beg); // 更改文件指针的偏移量
            content->resize(len);
            ifs.read(&(*content)[0], len);
            if (!ifs.good())
            {
                std::cout << __FILE__ << __LINE__ << "-"
                          << "read file content error" << std::endl;
                ifs.close();
                return false;
            }
            ifs.close();

            return true;
        }

        // 获取文件内容
        bool GetContent(std::string *content)
        {
            return GetPosLen(content, 0, FileSize());
        }

        // 写文件
        bool SetContent(const std::string &content)
        {
            std::ofstream ofs;
            ofs.open(filename_.c_str(), std::ios::binary);
            if (!ofs.is_open())
            {
                std::cout << __FILE__ << __LINE__ << "file open error" << std::endl;
                return false;
            }
            ofs.write(content.c_str(), content.size());
            if (!ofs.good())
            {
                std::cout << __FILE__ << __LINE__ << "file set content error" << std::endl;
                ofs.close();
            }
            ofs.close();
            return true;
        }

        //////////////////////////////////////////////
        // 压缩操作
        //  压缩文件
        bool Compress(const std::string &packname)
        {
            // 获取文件内容
            std::string body;
            if (!GetContent(&body))
            {
                std::cout << __FILE__ << __LINE__ << "Get file content error" << std::endl;
                return false;
            }

            std::string packed = bundle::pack(bundle::LZIP, body);

            // 将压缩的数据写入压缩包文件中
            FileUtil f(packname);
            if (!f.SetContent(packed))
            {
                std::cout << __FILE__ << __LINE__ << "Compress file error" << std::endl;
                return false;
            }
            return true;
        }

        // 解压
        bool UnCompress(const std::string &filename)
        {
            std::string body;
            if (!GetContent(&body))
            {
                std::cout << __FILE__ << __LINE__ << "Get file content error" << std::endl;
                return false;
            }

            std::string unpacked = bundle::unpack(body); // 解压->解压后数据放在unpacked中等待写入解压文件

            FileUtil f(filename);
            if (!f.SetContent(unpacked))
            {
                std::cout << __FILE__ << __LINE__ << "UnCompress file error" << std::endl;
                return false;
            }
            return true;
        }

        ///////////////////////////////////////////
        // 目录操作
        // 以下三个函数使用c++17中文件系统给的库函数实现
        bool Exists()
        {
            return fs::exists(filename_);
        }

        bool CreateDirectory()
        {
            if (Exists())
                return true;
            return fs::create_directories(filename_);
        }

        bool ScanDirectory(std::vector<std::string> *arry)
        {
            for (auto &p : fs::directory_iterator(filename_))
            {
                if (fs::is_directory(p) == true)
                    continue;
                // relative_path带有路径的文件名
                arry->push_back(fs::path(p).relative_path().string());
            }
            return true;
        }
    };

    class JsonUtil
    {
    public:
        static bool Serialize(const Json::Value &val, std::string *str)
        {
            // 建造者生成->建造者实例化json写对象->调用写对象中的接口进行序列化写入str
            Json::StreamWriterBuilder swb;
            std::unique_ptr<Json::StreamWriter> usw(swb.newStreamWriter());
            std::stringstream ss;
            if (usw->write(val, &ss) != 0)
            {
                std::cout << "serialize error" << std::endl;
                return false;
            }
            *str = ss.str();
            return true;
        }
        static bool UnSerialize(const std::string &str, Json::Value *val)
        {
            // 操作方法类似序列化
            Json::CharReaderBuilder crb;
            std::unique_ptr<Json::CharReader> ucr(crb.newCharReader());
            std::string err;
            if (ucr->parse(str.c_str(), str.c_str() + str.size(), val, &err) == false)
            {
                std::cout << "parse error" << std::endl;
                return false;
            }
            return false;
        }
    };
}
#endif
